package com.htudata.core.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import com.htudata.core.exception.ServiceException;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.Args;
import org.apache.http.util.EntityUtils;

public class HttpClientUtil {

  private static final String UTF_8 = "UTF-8";
  
  private static final String APPLICATION_JSON = "application/json";
  
  private static final int CONNECT_TIME_OUT = 60000;
  
  private static final int CONNECT_REQUEST_TIME_OUT = 60000;
  
  private static final int SOCKET_TIME_OUT = 180000;
  
  public static String httpGet(String url, Map<String, String> headers) throws ClientProtocolException, IOException, ServiceException, NoSuchAlgorithmException, KeyManagementException {
    return httpRequest(url, "GET", null, headers);
  }
  
  public static JSONObject httpGetJSONObject(String url, Map<String, String> headers) throws ClientProtocolException, IOException, ServiceException, KeyManagementException, NoSuchAlgorithmException {
    String content = httpGet(url, headers);
    JSONObject result = JSONObject.parseObject(content);
    return result;
  }
  
  public static JSONArray httpGetJSONArray(String url, Map<String, String> headers) throws ClientProtocolException, IOException, ServiceException, KeyManagementException, NoSuchAlgorithmException {
    String content = httpGet(url, headers);
    JSONArray result = JSONArray.parseArray(content);
    return result;
  }
  
  public static String httpPost(String url, String body, Map<String, String> headers) throws ClientProtocolException, IOException, ServiceException, NoSuchAlgorithmException, KeyManagementException {
    return httpRequest(url, "POST", body, headers);
  }
  
  public static HttpEntity httpPostRsEntity(String url, String body, Map<String, String> headers) throws ClientProtocolException, IOException, ServiceException, KeyManagementException, NoSuchAlgorithmException {
    return httpRequestRsEntity(url, "POST", body, headers);
  }
  
  public static JSONObject httpPostRESTReturnObject(String url, JSONObject jsonParam, Map<String, String> headers) throws ClientProtocolException, IOException, ServiceException, KeyManagementException, NoSuchAlgorithmException {
    String content = httpPost(url, (jsonParam != null) ? jsonParam
        .toString() : null, headers);
    JSONObject result = JSONObject.parseObject(content);
    return result;
  }
  
  public static JSONObject httpPostAddParamsReturnObject(String url, Map<String, Object> paramMap, Object bodyMap, Map<String, String> headers) throws ClientProtocolException, IOException, ServiceException, KeyManagementException, NoSuchAlgorithmException {
    if (paramMap != null) {
      url = url + "?";
      for (Map.Entry<String, Object> map : paramMap.entrySet())
        url = url + (String)map.getKey() + "=" + map.getValue() + "&"; 
      url = url.substring(0, url.length() - 1);
    } 
    String content = httpPost(url, (bodyMap != null) ? 
        JSONObject.toJSONString(bodyMap) : null, headers);
    JSONObject result = JSONObject.parseObject(content);
    return result;
  }
  
  public static JSONArray httpPostRESTReturnArray(String url, JSONObject jsonParam, Map<String, String> headers) throws ClientProtocolException, IOException, ServiceException, KeyManagementException, NoSuchAlgorithmException {
    String content = httpPost(url, (jsonParam != null) ? jsonParam
        .toString() : null, headers);
    JSONArray result = JSONArray.parseArray(content);
    return result;
  }
  
  public static String httpDelete(String url, String body, Map<String, String> headers) throws ClientProtocolException, IOException, ServiceException, NoSuchAlgorithmException, KeyManagementException {
    return httpRequest(url, "DELETE", body, headers);
  }
  
  public static JSONObject httpDeleteRESTReturnObject(String url, JSONObject jsonParam, Map<String, String> headers) throws ClientProtocolException, IOException, ServiceException, KeyManagementException, NoSuchAlgorithmException {
    String content = httpDelete(url, (jsonParam != null) ? jsonParam
        .toString() : null, headers);
    JSONObject result = JSONObject.parseObject(content);
    return result;
  }
  
  public static JSONArray httpDeleteRESTReturnArray(String url, JSONObject jsonParam, Map<String, String> headers) throws ClientProtocolException, IOException, ServiceException, KeyManagementException, NoSuchAlgorithmException {
    String content = httpDelete(url, (jsonParam != null) ? jsonParam
        .toString() : null, headers);
    JSONArray result = JSONArray.parseArray(content);
    return result;
  }
  
  public static String httpPut(String url, String body, Map<String, String> headers) throws ClientProtocolException, IOException, ServiceException, NoSuchAlgorithmException, KeyManagementException {
    return httpRequest(url, "PUT", body, headers);
  }
  
  public static JSONObject httpPutRESTReturnObject(String url, JSONObject jsonParam, Map<String, String> headers) throws ClientProtocolException, IOException, ServiceException, KeyManagementException, NoSuchAlgorithmException {
    String content = httpPut(url, (jsonParam != null) ? jsonParam
        .toString() : null, headers);
    JSONObject result = JSONObject.parseObject(content);
    return result;
  }
  
  public static JSONArray httpPutRESTReturnArray(String url, JSONObject jsonParam, Map<String, String> headers) throws ClientProtocolException, IOException, ServiceException, KeyManagementException, NoSuchAlgorithmException {
    String content = httpPut(url, (jsonParam != null) ? jsonParam
        .toString() : null, headers);
    JSONArray result = JSONArray.parseArray(content);
    return result;
  }
  
  public static String httpRequest(String url, String method, String body, Map<String, String> headers) throws ClientProtocolException, IOException, ServiceException, KeyManagementException, NoSuchAlgorithmException {
    HttpRequestBase request = getHttpRequest(method, url);
    setRequestConfig(request);
    if (headers != null && headers.size() > 0)
      for (String name : headers.keySet())
        request.addHeader(name, headers.get(name));  
    if (body != null) {
      StringEntity entity = new StringEntity(body, "UTF-8");
      entity.setContentEncoding("UTF-8");
      entity.setContentType("application/json");
      ((HttpEntityEnclosingRequestBase)request).setEntity((HttpEntity)entity);
    } 
    CloseableHttpResponse closeableHttpResponse = getHttpClient().execute((HttpUriRequest)request);
    String content = EntityUtils.toString(closeableHttpResponse.getEntity());
    int status = closeableHttpResponse.getStatusLine().getStatusCode();
    if (status == 200 || status == 401)
      return content; 
    throw new ServiceException(status, "");
  }
  
  public static HttpEntity httpRequestRsEntity(String url, String method, String body, Map<String, String> headers) throws ClientProtocolException, IOException, ServiceException, NoSuchAlgorithmException, KeyManagementException {
    HttpRequestBase request = getHttpRequest(method, url);
    setRequestConfig(request);
    if (headers != null && headers.size() > 0)
      for (String name : headers.keySet())
        request.addHeader(name, headers.get(name));  
    if (body != null) {
      StringEntity entity = new StringEntity(body, "UTF-8");
      entity.setContentEncoding("UTF-8");
      entity.setContentType("application/json");
      ((HttpEntityEnclosingRequestBase)request).setEntity((HttpEntity)entity);
    } 
    CloseableHttpResponse closeableHttpResponse = getHttpClient().execute((HttpUriRequest)request);
    int status = closeableHttpResponse.getStatusLine().getStatusCode();
    if (status == 200 || status == 401)
      return closeableHttpResponse.getEntity(); 
    throw new ServiceException(status, "");
  }
  
  public static <T> List<T> sendGetRequest(String url, Class<T> T) throws Exception {
    List<T> resultList = new ArrayList<>();
    JSONObject respObj = httpGetJSONObject(url, null);
    if (respObj == null)
      return resultList; 
    if (respObj.getInteger("status").intValue() != 0)
      return resultList; 
    resultList = JSON.parseArray(JSON.toJSONString(respObj.getJSONArray("data")), T);
    return resultList;
  }
  
  private static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
    SSLContext sc = SSLContext.getInstance("SSLv3");
    X509TrustManager trustManager = new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {}
        
        public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {}
        
        public X509Certificate[] getAcceptedIssuers() {
          return null;
        }
      };
    sc.init(null, new TrustManager[] { trustManager }, null);
    return sc;
  }
  
  private static CloseableHttpClient getHttpClient() throws KeyManagementException, NoSuchAlgorithmException {
    SSLContext sslcontext = createIgnoreVerifySSL();
    Registry socketFactoryRegistry =
             RegistryBuilder.create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", new SSLConnectionSocketFactory(sslcontext)).build();
    PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
    HttpClients.custom().setConnectionManager((HttpClientConnectionManager)connManager);
    return HttpClients.custom().setConnectionManager((HttpClientConnectionManager)connManager).build();
  }
  
  private static HttpRequestBase getHttpRequest(String method, String url) throws ServiceException {
    HttpGet httpGet;
    HttpPost httpPost;
    HttpDelete httpDelete;
    Args.notNull(url, "HTTP Request");
    Args.notNull(method, "HTTP Request method");
    HttpRequestBase request = null;
    String methodUpperCase = method.toUpperCase();
    switch (methodUpperCase) {
      case "GET":
        return (HttpRequestBase)new HttpGet(url);
      case "POST":
        return (HttpRequestBase)new HttpPost(url);
      case "DELETE":
        return (HttpRequestBase)new HttpDelete(url);
      case "PUT":
        return (HttpRequestBase)new HttpPut(url);
    } 
    throw new ServiceException(400, "暂不支持此类型方法的请求");
  }
  
  private static void setRequestConfig(HttpRequestBase request) {
    RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(60000).setConnectTimeout(60000).setSocketTimeout(180000).build();
    request.setConfig(requestConfig);
  }
}
